package com.ysten.basic.common.utils.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ysten.utils.bean.IEnumDisplay;

public class HttpUtil {
    public static final String UTF8 = "UTF-8";
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);
    private static final int DEFAULT_SOCKET_TIMEOUT = 5000;
    private static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    private static final long DEFAULT_CONNECT_MANAGER_TIMEOUT = 1000;
    private static final int SHORT_SOCKET_TIMEOUT = 1000;
    private static final int SHORT_CONNECT_TIMEOUT = 1000;
    private static final long SHORT_CONNECT_MANAGER_TIMEOUT = 1000;
    private static final int LONG_SOCKET_TIMEOUT = 60000;
    private static final int LONG_CONNECT_TIMEOUT = 30000;
    private static final long LONG_CONNECT_MANAGER_TIMEOUT = 30000;

    private HttpUtil() {
    }

    /**
     * post data to a given url.
     * 
     * @param url
     * @param nameValuePairs
     * @return
     * @throws HttpStatusCodeException
     * @throws IOException
     */
    public static String post(final String url, Map<String, String> nameValuePairs) throws HttpStatusCodeException,
            IOException {
        return post(url, nameValuePairs, UTF8);
    }

    /**
     * post data to a given url.
     * 
     * @param url
     * @param nameValuePairs
     * @return
     * @throws HttpStatusCodeException
     * @throws IOException
     */
    public static String post(final String url, Map<String, String> nameValuePairs, final String charset)
    		throws HttpStatusCodeException, IOException {
    	return post(url, nameValuePairs, charset, TimeoutType.DEFAULT);
    }
    
    public static String post(final String url, Map<String, String> nameValuePairs, final String charset, TimeoutType type)
    		throws HttpStatusCodeException, IOException {
    	HttpClient httpclient = null;
    	if (TimeoutType.SHORT.equals(type)) {
    		httpclient = createShortTimeoutHttpClient(charset);
        } else if (TimeoutType.LONG.equals(type)) {
        	httpclient = createLongTimeoutHttpClient(charset);
        } else {
        	httpclient = createDefaultHttpClient(charset);
        }
    	return post(url, nameValuePairs, charset, httpclient);
    }
    
    public static String post(final String url, Map<String, String> nameValuePairs, final String charset,
    		final HttpClient httpclient) throws HttpStatusCodeException, IOException {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("url:" + url);
            for (Entry<String, String> e : nameValuePairs.entrySet()) {
                LOGGER.info("params key:" + e.getKey() + ",value:" + e.getValue());
            }
        }
        HttpPost httpost = new HttpPost(url);
        try {
            if (nameValuePairs.size() > 0) {
                List<NameValuePair> nvps = new ArrayList<NameValuePair>(nameValuePairs.size());
                for (Entry<String, String> e : nameValuePairs.entrySet()) {
                    nvps.add(new BasicNameValuePair(e.getKey(), e.getValue()));
                }

                httpost.setEntity(new UrlEncodedFormEntity(nvps, charset));
            }
            
            return execute(httpclient, httpost, charset);
        } catch (UnsupportedEncodingException e) {
        	throw new IOException("in post UnsupportedEncodingException", e);
        }
    }
    
    /**
     * get data from a given url. if error return "";
     * @param url
     * @return
     * @throws HttpStatusCodeException
     * @throws IOException
     */
    public static String getData(final String url) {
    	try {
    		return get(url);
    	} catch (HttpStatusCodeException e) {
    		LOGGER.error("occur exception in get" + url + ": {} ", e);
    	} catch (IOException e) {
    		LOGGER.error("occur exception in get" + url + ": {} ", e);
    	} catch (Exception e) {
    		LOGGER.error("occur exception in get" + url + ": {} ", e);
    	}
    	return "";
    }

    /**
     * get data from a given url.
     * 
     * @param url
     * @return
     * @throws HttpStatusCodeException
     * @throws IOException
     */
    public static String get(final String url) throws HttpStatusCodeException, IOException {
        return get(url, UTF8, TimeoutType.DEFAULT);
    }

    /**
     * get data from a given url.
     * @param url
     * @param charset
     * @return
     * @throws HttpStatusCodeException
     * @throws IOException
     */
    public static String get(final String url, final String charset)
			throws HttpStatusCodeException, IOException {
    	return get(url, charset, TimeoutType.DEFAULT);
    }
    
    public static String get(final String url, final String charset, TimeoutType type)
			throws HttpStatusCodeException, IOException {
    	HttpClient httpclient = null;
    	if (TimeoutType.SHORT.equals(type)) {
    		httpclient = createShortTimeoutHttpClient(charset);
        } else if (TimeoutType.LONG.equals(type)) {
        	httpclient = createLongTimeoutHttpClient(charset);
        } else {
        	httpclient = createDefaultHttpClient(charset);
        }
    	return get(url, charset, httpclient);
    }
    
    public static String get(final String url, final String charset, final HttpClient httpclient)
    		throws HttpStatusCodeException, IOException {
        return execute(httpclient, new HttpGet(url), charset);
    }

    private static String execute(final HttpClient httpclient, final HttpUriRequest request, final String charset)
        throws HttpStatusCodeException, IOException
    {
        try
        {
            HttpResponse response = httpclient.execute(request);
            HttpEntity entity = response.getEntity();
            
            if (entity != null)
            {
                int statusCode = response.getStatusLine().getStatusCode();
                
                String body = EntityUtils.toString(entity, charset);
                
                if (statusCode == HttpStatus.SC_OK)
                {
                    return body;
                }
                else
                {
                    throw new HttpStatusCodeException(statusCode, body);
                }
            }
            else
            {
                throw new IOException("get null content for this request");
            }
        }
        catch (ClientProtocolException e)
        {
            LOGGER.error("ClientProtocolException:" + request.getProtocolVersion(), e);
            throw e;
        }
        finally
        {
            httpclient.getConnectionManager().shutdown();
        }
    }

    private static HttpClient createDefaultHttpClient(final String charset) {
    	return createHttpClient(charset, DEFAULT_SOCKET_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, DEFAULT_CONNECT_MANAGER_TIMEOUT);
    }
    
    private static HttpClient createLongTimeoutHttpClient(final String charset) {
    	return createHttpClient(charset, LONG_SOCKET_TIMEOUT, LONG_CONNECT_TIMEOUT, LONG_CONNECT_MANAGER_TIMEOUT);
    }
    
    private static HttpClient createShortTimeoutHttpClient(final String charset) {
    	return createHttpClient(charset, SHORT_SOCKET_TIMEOUT, SHORT_CONNECT_TIMEOUT, SHORT_CONNECT_MANAGER_TIMEOUT);
    }
    
    public static HttpClient createHttpClient(int socketTimeout, int connTimeout) {
        return createHttpClient(UTF8, socketTimeout, connTimeout, DEFAULT_CONNECT_MANAGER_TIMEOUT, false);
    }
    
    public static HttpClient createHttpClient(final String charset, int socketTimeout,
    		int connTimeout, long connManagerTimeout) {
        return createHttpClient(charset, socketTimeout, connTimeout, connManagerTimeout, false);
    }
    
    public static HttpClient createHttpClient(final String charset, int socketTimeout,
    		int connTimeout, long connManagerTimeout, boolean isGZIP) {
        // SchemeRegistry supportedSchemes = new SchemeRegistry();
        // SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
        // supportedSchemes.register(new Scheme("http", 80, sf));
        //
        // // Prepare parameters.
        // // Since this example doesn't use the full core framework,
        // // only few parameters are actually required.
        HttpParams params = new SyncBasicHttpParams();
        params.setParameter(HttpConnectionParams.SO_TIMEOUT, socketTimeout);
        params.setParameter(HttpConnectionParams.CONNECTION_TIMEOUT, connTimeout);
        params.setParameter(ClientPNames.CONN_MANAGER_TIMEOUT, connManagerTimeout);
        HttpProtocolParams.setContentCharset(params, charset);

        // HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        // HttpProtocolParams.setUseExpectContinue(params, false);
        //
        // ClientConnectionManager clcm = new
        // ThreadSafeClientConnManager(supportedSchemes);
        // DefaultHttpClient httpclient = new DefaultHttpClient(clcm);
        
        DefaultHttpClient httpclient = new DefaultHttpClient(params);
        
        // do gzip
        if (isGZIP) {
        	httpclient = GZIPHttpClient(httpclient);
        }

        return httpclient;
    }
    
    public static InputStream post(final String url, final String xmlValueParam, final String charset)
			throws HttpStatusCodeException, IOException {
    	return post(url, xmlValueParam, charset, TimeoutType.DEFAULT);
    }
    
	public static InputStream post(final String url, final String xmlValueParam, final String charset, TimeoutType type)
			throws HttpStatusCodeException, IOException {
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("url:" + url);
			LOGGER.info("xmlValueParam:" + xmlValueParam);
		}
		HttpPost httpost = new HttpPost(url);
		StringEntity reqEntity = new StringEntity(xmlValueParam, charset);
		httpost.addHeader("Content-type", "text/xml");
		httpost.setEntity(reqEntity);
		
		if (TimeoutType.SHORT.equals(type)) {
        	return executePost(createShortTimeoutHttpClient(charset), httpost, charset);
        } else if (TimeoutType.LONG.equals(type)) {
        	return executePost(createLongTimeoutHttpClient(charset), httpost, charset);
        } else {
        	return executePost(createDefaultHttpClient(charset), httpost, charset);
        }
	}

	private static InputStream executePost(HttpClient httpclient, final HttpPost httpost, final String charset)
			throws HttpStatusCodeException, IOException {
		HttpResponse response = httpclient.execute(httpost);
		HttpEntity entity = response.getEntity();

		if (entity != null) {
			int statusCode = response.getStatusLine().getStatusCode();

			InputStream body = entity.getContent();

			if (statusCode == HttpStatus.SC_OK) {
				return body;
			} else {
				throw new HttpStatusCodeException(statusCode, EntityUtils.toString(entity, charset));
			}
		} else {
			throw new IOException("get null content for this post");
		}
	}
	
	private static DefaultHttpClient GZIPHttpClient(DefaultHttpClient httpclient) {
		httpclient.addRequestInterceptor(new HttpRequestInterceptor() {
            public void process(final HttpRequest request, final HttpContext context) throws HttpException,
                    IOException {
                if (!request.containsHeader("Accept-Encoding")) {
                    request.addHeader("Accept-Encoding", "gzip");
                }
            }
        });

        httpclient.addResponseInterceptor(new HttpResponseInterceptor() {
            public void process(final HttpResponse response, final HttpContext context) throws HttpException,
                    IOException {
                HttpEntity entity = response.getEntity();
                Header ceheader = entity.getContentEncoding();
                if (ceheader != null) {
                    HeaderElement[] codecs = ceheader.getElements();
                    for (int i = 0; i < codecs.length; i++) {
                        if (codecs[i].getName().equalsIgnoreCase("gzip")) {
                            response.setEntity(new GzipDecompressingEntity(response.getEntity()));
                            return;
                        }
                    }
                }
            }
        });
        return httpclient;
	}
	
	public enum TimeoutType implements IEnumDisplay {
    	SHORT("short"), LONG("long"), DEFAULT("default");
    	private String msg;
    	private TimeoutType(String msg){
    		this.msg = msg;
    	}
    	@Override
    	public String getDisplayName() {
    		return this.msg;
    	}
    }
}
